package com.ting.encrypt.interceptor;


import com.ting.common.enums.ResultEnum;
import com.ting.common.exception.TingBaseException;
import com.ting.encrypt.annotation.SensitiveEncryptEnabled;
import com.ting.encrypt.annotation.SensitiveField;
import com.ting.encrypt.enums.SensitiveType;
import com.ting.encrypt.factory.SensitiveHandlerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author: guohaibing
 * @date: 2022/10/12 10:50
 * @description: 对response内容进行脱敏操作
 */
@ControllerAdvice
@Slf4j
public class SensitiveResponseInterceptor implements ResponseBodyAdvice<Object> {

  @Override
  public boolean supports(
      MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
    return (AnnotatedElementUtils.hasAnnotation(methodParameter.getContainingClass(), ResponseBody.class) ||
            methodParameter.hasMethodAnnotation(ResponseBody.class));
  }

  @Override
  public Object beforeBodyWrite(
      Object body,
      MethodParameter methodParameter,
      MediaType mediaType,
      Class<? extends HttpMessageConverter<?>> aClass,
      ServerHttpRequest serverHttpRequest,
      ServerHttpResponse serverHttpResponse) {
    SensitiveField methodAnnotation = methodParameter.getMethodAnnotation(SensitiveField.class);
    if (!isBaseType(body)) {
      if (methodAnnotation != null && body instanceof CharSequence) { // 返回的是单个字符串
        SensitiveType value = methodAnnotation.value();
        if (body instanceof CharSequence) { // 返回单个字符串
          body = SensitiveHandlerFactory.getSensitiveTypeHandler(value).handle(body);
        }
      } else {
        Class clazz = getGenericParameterType(methodParameter.getGenericParameterType());// 获取返回值泛型类型
        Annotation classAnnotation = clazz.getAnnotation(SensitiveEncryptEnabled.class);
        body = sensitiveBodyObject(body, methodAnnotation,classAnnotation!=null);
      }
    }
    return body;
  }
  
  private Class getGenericParameterType(Type genericParameterType) {
    if(genericParameterType instanceof ParameterizedType) {
      Type actualTypeArgument1 = ((ParameterizedType) genericParameterType).getActualTypeArguments()[0];
      return getGenericParameterType(actualTypeArgument1);
    } else {
      return (Class)genericParameterType;
    }
  }
  
  private Object sensitiveBodyObject(Object body, SensitiveField methodAnnotation,boolean flag) {
    if (body instanceof Collection) { // 返回集合
      Collection collection = (Collection) body;

      if (collection.size() > 0) {
        Object[] objects = collection.toArray();
        body = analysisArrObjects(body, methodAnnotation, objects);
      }

    } else if (body instanceof Object[]) { // 返回数组
      Object[] objects = (Object[]) body;
      if (objects != null && objects.length > 0) {
        body = analysisArrObjects(body, methodAnnotation, objects);
      }
    } else if(flag) { // 返回单对象
      analysisBodyObject(body);
    }
    return body;
  }

  /**
   * 处理返回集合或数组
   *
   * @param body
   * @param methodAnnotation
   * @param objects
   * @return
   */
  private Object analysisArrObjects(
      Object body, SensitiveField methodAnnotation, Object[] objects) {
    if (objects != null && objects.length > 0) {

      if ((objects[0] instanceof CharSequence) && methodAnnotation != null) { // 是字符序列
        if (body instanceof Set) {
          body =
              Arrays.stream(objects)
                  .map(
                      object ->
                          SensitiveHandlerFactory.getSensitiveTypeHandler(methodAnnotation.value())
                              .handle(object))
                  .collect(Collectors.toSet());

        } else if (body instanceof List) {
          body =
              Arrays.stream(objects)
                  .map(
                      object ->
                          SensitiveHandlerFactory.getSensitiveTypeHandler(methodAnnotation.value())
                              .handle(object))
                  .collect(Collectors.toList());

        } else if (body instanceof String[]) {
          for (int i = 0; i < objects.length; i++) {
            objects[i] =
                SensitiveHandlerFactory.getSensitiveTypeHandler(methodAnnotation.value())
                    .handle(objects[i]);
          }
          body = objects;
        } else {
          log.warn("not support collection type!");
        }
      } else if (!isBaseType(objects[0])) { // 不是基本数据类型
        for (int i = 0; i < objects.length; i++) {
          analysisBodyObject(objects[i]);
        }
      }
    }
    return body;
  }

  /**
   * 解析对象
   *
   * @param body
   */
  private void analysisBodyObject(Object body) {
    try {

      if (body == null) return;

      Class<?> bodyClass = body.getClass();

      // 判断类上是否有 SensitiveEncryptEnabled 注解
      SensitiveEncryptEnabled annotation = bodyClass.getAnnotation(SensitiveEncryptEnabled.class);
      if (annotation != null) {
        Field[] declaredFields = bodyClass.getDeclaredFields();

        for (Field field : declaredFields) {
          field.setAccessible(true);
          Object fieldValue = field.get(body);

          // 普通字段脱敏
          SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
//          SensitiveJSONField sensitiveJSONField = field.getAnnotation(SensitiveJSONField.class);
          if (sensitiveField != null) {
            if (fieldValue instanceof Collection) {
              Object[] objects = ((Collection<?>) fieldValue).toArray();
              Object newValue = analysisArrObjects(fieldValue, sensitiveField, objects);
              field.set(body, newValue);

            } else if (fieldValue instanceof String[]) {
              Object[] objects = (String[]) fieldValue;
              Object newValue = analysisArrObjects(fieldValue, sensitiveField, objects);
              field.set(body, newValue);
              
            } else {
              String newValue =
                  SensitiveHandlerFactory.getSensitiveTypeHandler(sensitiveField.value())
                      .handle(fieldValue);
              field.set(body, newValue);
            }
//          } else if (sensitiveJSONField != null) { // json字段脱敏
//            SensitiveJSONFieldKey[] sensitiveJSONFieldKeys = sensitiveJSONField.sensitivelist();
//            String newValue =
//                JsonUtil.sensitiveKeyword(String.valueOf(fieldValue), sensitiveJSONFieldKeys);
//            field.set(body, newValue);
          } else if (field.getAnnotation(SensitiveEncryptEnabled.class) != null) { // 这个属性代表的类是否需要加密
            analysisBodyObject(fieldValue);
          }
        }
      } else {
        sensitiveBody(body, bodyClass);
      }
    } catch (Exception e) {
      log.error("数据脱敏失败", e);
      throw new TingBaseException(ResultEnum.SENSITIVE_ERROR);
    }
  }
  
  private void sensitiveBody(Object body, Class<?> bodyClass) throws IllegalAccessException {
    Field[] declaredFields = bodyClass.getDeclaredFields();
    
    for (Field field : declaredFields) {
      field.setAccessible(true);
      Object fieldValue = field.get(body);
      if (!(fieldValue instanceof CharSequence) && !isBaseType(fieldValue)) {
        sensitiveBodyObject(fieldValue, null,true);
      }
    }
    Class<?> superclass = bodyClass.getSuperclass();
    if(!(superclass.getName().equals("java.lang.Object"))) {
      sensitiveBody(body,superclass);
    }
    return;
  }
  
  /**
   * 判断是不是基本数据类型
   *
   * @param body
   * @return
   */
  private Boolean isBaseType(Object body) {
    if (body == null
        || body instanceof Byte
        || body instanceof Short
        || body instanceof Integer
        || body instanceof Long
        || body instanceof Float
        || body instanceof Double
        || body instanceof Character
        || body instanceof Boolean) {
      return true;
    }
    return false;
  }
}
